#
# Sorto, the sorting game
# Copyright (C) 2012 Ralph Preston
# ralph.preston@gmail.com
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to:
#
# Free Software Foundation, Inc.
# 51 Franklin Street, Fifth Floor
# Boston, MA 02110-1301
# USA.
#
import sys

from sorto_base_view  import SortoBaseView
from sorto_board      import SortoBoard
from sorto_controller import SortoController

class SortoConsole(SortoBaseView):

    def __init__(self, max_games, logfile=None, append=False, players=None):
        SortoBaseView.__init__(self, max_games, logfile, append, 2)
        self._winning_round   = 0
        self._player_points   = {}
        self._player_counters = {}
        self._player_boards   = {}
        self._chosen_counter  = None
        self._die_roll        = None
        return

    def get_player_names(self):
        if self.controller is None:
            return ''
        return self.controller.get_player_names()

    def get_max_games(self):
        if self.max_games:
            return self.max_games

        while 1:
            input = raw_input("How may games to simulate? ")
            if input:
                try:
                    self.max_games = int(input)
                except Exception:
                    continue
                break
            pass

        return self.max_games

    def set_seed(self):
        input = raw_input("Seed: ")
        self._initial_seed = long(input, 16)
        print "Seed set to:", hex(self._initial_seed)
        self.controller.set_test_seed(self._initial_seed)
        return

    def set_game_number(self, game_number):
        SortoBaseView.set_game_number(self, game_number)
        if self.verbosity > 0:
            print "Simulating game %d of %d" % (game_number, self.max_games)
            pass
        return

    def start_remote_server(self):
        input = raw_input("Servers IP: [127.0.0.1] ")
        if not input:
            input = '127.0.0.1'
            pass
        port = raw_input("Server Port: [8000] ")
        if not port:
            port = '8000'
            pass
        self.controller.start_remote_server(input, port)
        return

    def set_round(self, round):
        SortoBaseView.set_round(self, round)
        if self.verbosity > 1:
            print "Round", round
            pass
        return

    def set_board_state(self, player_name, player_colors, board, counters):
        SortoBaseView.set_board_state(self, player_name, player_colors, 
                                      board, counters)
        if self.verbosity > 1:
            print player_name
            if counters:
                print "  Counters " + ', '.join([str(x) for x in counters])
                pass
            print board
            pass
        return

    def set_die_roll(self, die_roll):
        self._die_roll = die_roll
        if self.verbosity > 1:
            print "The roll is", die_roll
            pass
        return

    def set_rounds_until_win(self, round):
        self._winning_round = round
        return
    
    def set_winner(self, player_name):
        if self.verbosity > 1:
            print "  %s won on round %d" % (player_name, self._winning_round)
            pass
        return

    def set_points_won(self, player_name, points):
        prev_total = self._player_points.get(player_name, 0)
        self._player_points[player_name] = prev_total + points
        if self.verbosity > 1:
            print "  %s has %d points" % (player_name, self._player_points[player_name])
            pass
        return

    def set_simulation_complete(self):
        print "Simulation over"
        # Create a new SortoController because threads cannot be
        # restarted, according to RuntimeError.
        if self.controller:
            self.controller.abort()
            self.controller = None
            pass
        self.controller = SortoController(view=self, logfile=self.logfile)
        for name in self.player_files:
            self.controller.load_player(self.player_files[name], quiet=True)
            pass
        for name in self.remote_player_files:
            (player_file, server) = self.remote_player_files[name]
            self.controller.load_remote_player(player_file, server)
            pass
        return

    def display_text(self, text):
        print text
        return

    def choose_counter(self, player_name, counter_from):
        self._active_player = player_name
        return SortoBaseView.choose_counter(self, player_name, counter_from)

    def display_board_for_player(self, player_name):
        f_board = self._player_boards[player_name]
        print f_board
        return

    def get_counter_from(self, counter_from):
        counter_list = []
        if counter_from is self.OFF_BOARD_COUNTER:
            counter_list = self._player_counters[self._active_player]
            pass
        else:
            board = self._player_boards[self._active_player]
            counter_list = board.get_counters()
            pass

        return self._choose_counter_from_list(counter_list)

    def _choose_counter_from_list(self, counters):
        # If there is only one counter the choice is easy.
        if len(counters) == 1:
            counter = counters[0]
            self._chosen_counter = counter
            # self._player_counters[self._active_player] = []
            print "%s must playing counter %d" % (self._active_player, counter)
            return counter

        print self._active_player,"choose counter", ', '.join([str(x) for x in counters])," ?"
        while 1:
            val = raw_input('> ')
            if val:
                try:
                    val = int(val)
                except ValueError:
                    if val.lower() == 'q':
                        sys.exit(0)
                    print "Enter the counter number."
                    pass
                if val in counters:
                    counter = val
                    break
                pass
            pass
        self._chosen_counter = counter
        return counter

    def choose_game_board_counter(self, player_name):
        while 1:
            val = raw_input("%s Choose a counter to move: " % player_name)
            if val:
                try:
                    val = int(val)
                except ValueError:
                    if val.lower() == 'q':
                        sys.exit(0)
                    print "Enter the counter number."
                    pass
                if val > 0 and val < 13:
                    counter = val
                    break
                pass
            pass
        self._chosen_counter = counter
        return counter

    def place_counter(self, player_name):
        board = self._player_boards[player_name]
        legal_places = board.legal_places_for_roll_and_counter(self._die_roll,
                                                               self._chosen_counter)
        print "legal places:", legal_places
 
        while 1:
            loc = raw_input('%s place the counter on index 0-35: ' % player_name)
            if loc:
                try:
                    val = int(loc)
                except ValueError:
                    if loc.lower() == 'q':
                        sys.exit(0)
                        pass
                    pass
                if val >= 0 and val < 36:
                    break
                pass
            pass
        return val

    def undo_place_counter(self):
        print "Illegal move"
        return

    def accept_place_counter(self):
        return

    def load_player(self):
        input = raw_input("SortoPlayer file: [sorto_player.py] ")
        if not input:
            input = 'sorto_player.py'
            pass
        player_file = input
        local = True
        input = raw_input("Local or Remote? [l]/r: ")
        if input and input.lower()[0] == 'r':
            local = False
            pass
        
        if local:
            player_name = self.controller.load_player(player_file)
            self.player_files[player_name] = player_file
        else:
            server_address = "localhost:8000"
            input = raw_input("Remote server address: [%s] " % server_address)
            if input:
                server_address = input
                pass
            player_name = self.load_remote_player(player_file, server_address)
            self.remote_player_files[player_name] = (player_file, 
                                                     server_address)
            pass
        return 

    def add_remote_player(self, player_name, server_address):
        self.remote_player_files[player_name] = (player_file, 
                                                 server_address)
        print "%s joined the game" % player_name
        return

    def clear_player(self):
        self.controller.clear_players()
        self.player_files = {}
        self.remote_player_files = {}
        return

    def set_game_count(self):
        while 1:
            input = raw_input("Number of games to simulate: ")
            if input:
                try:
                    games = int(input.strip())
                    break
                except ValueError:
                    pass
                pass
            pass
        self.max_games = games
        return

    def set_logging(self):
        input = raw_input("Set logfile: ")
        if not input:
            self.logfile = None
        else:
            self.logfile = input
            pass
        return

    def set_verbosity(self):
        while 1:
            print "Set verbosity:"
            print "  0 - no simulation output until the simultation completes"
            print "  1 - the game number is output"
            print "  2 - all information is output [default]"
            input = raw_input("> ")
            if not input:
                continue
            
            try:
                val = int(input)
            except ValueError:
                pass
            
            if val < 0 or val > 2:
                continue
            break
        self.verbosity = val
        return

    def start(self):
        self.controller.set_logfile(self.logfile)
        print "Simulation running..."
        self.controller.start()

        real = False
        real_player_file = 'sorto_real_player.py'
        for player_name in self.player_files:
            if self.player_files[player_name] == real_player_file:
                real = True
                pass
            pass

        for player_name in self.remote_player_files:
            (player_file, addr) = self.remote_player_files[player_name]
            if player_file == real_player_file:
                real = True
                pass            
            pass

        if real:
            self.controller.join()
            self.controller = SortoController(view=self, logfile=self.logfile)
            pass
        
        return

    def abort(self):
        if self.controller:
            self.controller.abort()
            self.controller = None
            pass
        return

    def warranty(self):
        print "This program is distributed in the hope that it will be useful,"
        print "but WITHOUT ANY WARRANTY; without even the implied warranty of"
        print "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the"
        print "GNU General Public License for more details."
        return

    def conditions(self):
        print "This program is free software; you can redistribute it and/or"
        print "modify it under the terms of the GNU General Public License"
        print "as published by the Free Software Foundation; either version 2"
        print "of the License, or (at your option) any later version."
        return

    pass # End of class SortoConsole

class Command(object):
    def __init__(self, cmd_str, command):
        self._command_str = cmd_str
        self.command = command
        return

    def __str__(self):
        return self._command_str

    pass # End of class Command


if __name__ == '__main__':
    from optparse import OptionParser

    parser = OptionParser()
    parser.add_option("-n", "--number-of-games",
                      type="int", 
                      default=1,
                      help="The number of games to simulate")
    parser.add_option("-p", "--player",
                      type="string",
                      default='sorto_player.py',
                      help="Path to the python file containing a sorto_player class")
    parser.add_option("-l", "--logfile",
                      type="string",
                      default='playlog.log',
                      help="Log the plays to a logfile")
    parser.add_option("-a", "--append",
                      action="store_true",
                      default=False,
                      help="If logging to a file and it already exists, append to the end")    

    (args, extra) = parser.parse_args()

    print
    print "Sorto v1, Copyright (C) 2012 Ralph Preston"
    print "Sorto comes with ABSOLUTELY NO WARRANTY; for details"
    print "type `Show warranty'.  This is free software, and you are welcome"
    print "to redistribute it under certain conditions; type `Show conditions'"
    print "for details."
    print

    players = args.player
    if not players:
        while 1:
            input = raw_input("sorto_player file: ")
            if input:
                players = input
                break
            pass
        pass

    console = SortoConsole(args.number_of_games, 
                           args.logfile, 
                           args.append, 
                           players)

    def quit():
        console.abort()
        sys.exit(0)

    commands = []
    commands.append(Command('Show warranty', console.warranty))
    commands.append(Command('Show conditions', console.conditions))
    commands.append(Command('Start remote server', console.start_remote_server))
    commands.append(Command('Set seed...', console.set_seed))    
    commands.append(Command('Load player...', console.load_player))
    commands.append(Command('Clear players', console.clear_player))
    commands.append(Command('Set game count...', console.set_game_count))
    commands.append(Command('Set logging...', console.set_logging))
    commands.append(Command('Set verbosity...', console.set_verbosity))
    commands.append(Command('Start simulation', console.start))
    commands.append(Command('Quit', quit))

    while 1:
        print
        print "Players are", ', '.join(console.get_player_names())
        print
        for ii in range(len(commands)):
            command = commands[ii]
            print "%d) %s" % (ii+1, str(command))
            pass
        choice = raw_input("> ")
        if not choice:
            continue
        if choice.lower() == 'q':
            break

        # Check if the user input the command string or number
        val = None
        try:
            val = int(choice) - 1
        except ValueError:
            pass
        if val is not None: # val is set if the choice was a number
            if val < len(commands):
                commands[val].command()
                pass
            continue
        
        choice = choice.lower()
        for cmd in commands:
            if str(cmd).lower() == choice:
                cmd.command()
                break
            pass
        pass

    console.abort()

    sys.exit(0)

    pass


